//
// Created by wang on 2021/7/4.
//

#include "sys.h"
#include <winsock2.h>
#include <iphlpapi.h>
#include "eth.h"

#define WORKING_BUFFER_SIZE 15000
#define MAX_TRIES 3


uint32_t pil_getpid() {
    return GetCurrentProcessId();
}

ret_t pil_device_mac(mac mac_buf) {
    ULONG ret = ENET_RET_SUCCESS;
    PIP_ADAPTER_ADDRESSES p_address = NULL;
    PIP_ADAPTER_ADDRESSES p_currAddresses = NULL;
    uint32_t iterations = 0;
    ULONG out_buf_len = 0;
    uint32_t family = AF_UNSPEC;
    uint32_t flags = GAA_FLAG_INCLUDE_PREFIX;

    if(mac_buf == NULL)
        return ERR_ERR_INVALID_PARAMETER;

    out_buf_len = WORKING_BUFFER_SIZE;
    do {
        p_address = (IP_ADAPTER_ADDRESSES *) malloc(out_buf_len);
        if (NULL == p_address) {
            return ERR_PIL_GET_DEVICE_MAC_FAIL;
        }

        ret = GetAdaptersAddresses(
                family, flags, NULL, p_address, &out_buf_len);

        if (ret == ERROR_BUFFER_OVERFLOW) {
            free(p_address);
            p_address = NULL;
        } else {
            break;
        }

        iterations ++;
    } while (iterations < MAX_TRIES);

    if (NO_ERROR == ret) {
        p_currAddresses = p_address;
        while (p_currAddresses) {
            if (0 != p_currAddresses->PhysicalAddressLength) {
                memcpy(mac_buf, p_currAddresses->PhysicalAddress, MAC_LEN);
                if (!ETH_IS_ZERO_MAC(mac_buf)) {
                    ret = ENET_RET_SUCCESS;
                    goto out;
                }
            }
            p_currAddresses = p_currAddresses->Next;
        }
    }

    out:
    if (p_address)
        free(p_address);
    return ret;
}

#define PIL_WIN_PIPE_BUF_SIZE 4096
ret_t pil_create_pipe(const char *name, uint8_t acc,
                         uint32_t mode, char *pipe_desc) {

    DWORD p = 0;
    DWORD m = PIPE_WAIT;
    char pipe_name[PIL_PIPE_DESC_LEN] = "\\\\.\\pipe\\";

    if (name == NULL)
        return ERR_PIL_INVALID_PARAMETERS;

    strcat_s(pipe_name, sizeof(pipe_name), name);

    if (acc & PIL_PIPE_ACC_R)
        p |= PIPE_ACCESS_INBOUND;
    if (acc & PIL_PIPE_ACC_W)
        p |= PIPE_ACCESS_OUTBOUND;

    if (mode == PIL_PIPE_NONBLOCK)
        m = PIPE_NOWAIT;

    if (CreateNamedPipeA(pipe_name, p, PIPE_TYPE_MESSAGE | m,
                         PIPE_UNLIMITED_INSTANCES, PIL_WIN_PIPE_BUF_SIZE,
                         PIL_WIN_PIPE_BUF_SIZE, 0, NULL) < 0) {
        return ERR_PIL_CREATE_PIPE_FAILED;
    }

    strcpy(pipe_desc, pipe_name);
    return ENET_RET_SUCCESS;
}

ret_t pil_connect_pipe(
        const char *pipe_desc, uint8_t perm, pil_pipe_handle *handle) {

    HANDLE h_Pipe;
    DWORD p = 0;
    uint32_t ret = ENET_RET_SUCCESS;

    if (perm & PIL_PIPE_R)
        p |= GENERIC_READ;
    if (perm & PIL_PIPE_W)
        p |= GENERIC_WRITE;

    h_Pipe = CreateFile(pipe_desc, p, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (h_Pipe != INVALID_HANDLE_VALUE)
    {
        handle->win_handle = h_Pipe;
    }

    if (GetLastError() == ERROR_FILE_NOT_FOUND) {
        ret = ERR_PIL_PIPE_NOT_FOUND;
    } else {
        ret = ERR_PIL_CONNECT_PIPE_FAILED;
    }

    return ret;
}

ret_t pil_read_pipe(pil_pipe_handle *handle, byte *buf, uint32_t len,
                       unsigned long *read_len) {
    BOOL f_success;

    f_success = ReadFile(handle->win_handle, buf, len, read_len, NULL);

    if (!f_success || *read_len == 0) {
        return ERR_PIL_READ_PIPE_FAILED;
    }

    return ENET_RET_SUCCESS;
}

ret_t pil_write_pipe(pil_pipe_handle *handle, byte *buf, uint32_t len,
                        unsigned long *write_len) {
    BOOL f_success;

    f_success = WriteFile(handle->win_handle, buf, len, write_len, NULL);

    if (!f_success || *write_len != len) {
        return ERR_PIL_WRITE_PIPE_FAILED;
    }

    return ENET_RET_SUCCESS;
}

void pil_sleep(uint32_t milliseconds) {
    Sleep(milliseconds);
}
